CloudFront-Forwarded-Protoヘッダを使ってCloudFrontへの通信プロトコルをオリジン側で確認してみた
はじめに
清水です。前回のエントリに続いて、今回もCloudFront-ELB-EC2な構成でのネタです。ユーザからの通信がHTTPSまたはHTTPだとして、かつCloudFront-ELB間はHTTPS通信と設定した環境とします。オリジンであるEC2側ではユーザからCloudFrontへの通信プロトコルをどのように判別すればよいか調査してみました。
ELB-EC2のような構成でEC2側でユーザからELBへの通信プロトコルを確認する場合はX-Forwarded-Protoヘッダを使用します。
ただ今回のようなCloudFront-ELB-EC2の構成で、さらにはCloudFrontーELB間についてはHTTPS通信となるよう設定した場合については、X-Forwarded-Proto
ヘッダはユーザの通信プロトコルにかかわらずHTTPSになってしまいます。
ユーザからCloudFrontへの通信プロトコルを確認するには、CloudFrontにより追加されるCloudFront-Forwarded-Protoヘッダを確認する必要があります。またCloudFrontでヘッダを転送しないよう設定している場合はこのCloudFront-Forwarded-Proto
ヘッダもオリジンに転送されませんので、CloudFront-Forwarded-Proto
ヘッダだけでも転送するよう設定する必要があります。
本エントリではこのCloudFront-Forwarded-Protoヘッダのオリジンへの転送方法と、実際にオリジンであるEC2上でどのように確認できるのかをまとめてみます。なお検証に用いたEC2側ではApache+PHPが稼働し、以下のコードでEC2側でリクエストに含まれるヘッダをすべて出力するようにしています。
<?php foreach (getallheaders() as $name => $value) { echo "$name: $value\n"; } ?>
CloudFront-Forwarded-Protoヘッダをオリジンに転送しない場合
まずはCloudFrontの設定でヘッダを転送しない設定での挙動を確認してみます。CloudFront側ではCache Based on Selected Request Headers
がNone(Improves Caching)
と設定してある状態ですね。
まずはHTTPSでのアクセスです。上半分はcurlコマンドで出力しているヘッダ情報、1行挟んで下半分がオリジンで出力しているヘッダ情報です。オリジン側ではX-Forwarded-Proto: httpsまでは確認できますね。
$ curl -i https://www.example.com/ HTTP/2 200 content-type: text/html; charset=UTF-8 content-length: 360 date: Tue, 31 Jul 2018 12:12:37 GMT server: Apache/2.4.27 (Amazon) PHP/5.6.35 x-powered-by: PHP/5.6.35 x-cache: Miss from cloudfront via: 1.1 ab7b99dd2576a3bf9957a9db22bcccdd.cloudfront.net (CloudFront) x-amz-cf-id: w0PQfLbYhJuDyVxiN9QOYOh9DVPOuyJhFTqI84XhoVBJy5cQ79TtCA== X-Forwarded-For: 1.XX.XX.XX, 13.113.203.179 X-Forwarded-Proto: https X-Forwarded-Port: 443 Host: elb.example.com X-Amzn-Trace-Id: Root=1-5b605235-e68654a09897695ee2937990 X-Amz-Cf-Id: RqK1mWNDkHje_j0o9nQvZ2L80wa1YF9B0f40-WOfN_zr0pA_GzCwug== User-Agent: Amazon CloudFront Via: 2.0 ab7b99dd2576a3bf9957a9db22bcccdd.cloudfront.net (CloudFront)
続いてHTTPでのアクセスです。なおキャッシュ期間を調整してキャッシュがなくなるよう設定してあります。X-Forwarded-Proto
をみるとhttpsとなっていますね。これはELBからのHTTPS通信を行っているためです。
$ curl -i http://www.example.com/ HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8 Content-Length: 359 Connection: keep-alive Date: Tue, 31 Jul 2018 12:13:49 GMT Server: Apache/2.4.27 (Amazon) PHP/5.6.35 X-Powered-By: PHP/5.6.35 X-Cache: Miss from cloudfront Via: 1.1 fbc942e4bd97accc585402ff91f07cbb.cloudfront.net (CloudFront) X-Amz-Cf-Id: aKLHcNpD_zpf8BJRNZCZW1arw0b3ob_uz9Vg7pkCQ-k8Zto6XRFDUQ== X-Forwarded-For: 1.XX.XX.XX, 13.113.203.53 X-Forwarded-Proto: https X-Forwarded-Port: 443 Host: elb.example.com X-Amzn-Trace-Id: Root=1-5b60527d-97a73e6073816cbc856f9d18 X-Amz-Cf-Id: B0husBFBqpX73xfPYDOEz92BrZQ1A-LjS9BujTlj-Orl5AApxVJQxQ== User-Agent: Amazon CloudFront Via: 1.1 fbc942e4bd97accc585402ff91f07cbb.cloudfront.net (CloudFront)
ということで、CloudFront-Forwarded-Proto
ヘッダもありませんし、ユーザからCloudFrontへの通信プロトコルの判断をオリジンEC2で行うことは難しそうです。
CloudFront-Forwarded-Protoヘッダをオリジンに転送するよう設定する
CloudFrontへの通信プロトコルを確認するために、CloudFront-Forwarded-Proto
ヘッダを転送するようディストリビューションを設定します。Cache Based on Selected Request Headers
の項目でWhitelist
を選びCloudFront-Forwarded-Proto
を選択します。
要件等によってすべてのヘッダを転送するように設定しても、CloudFront-Forwarded-Proto
も転送することになりますので、同様の効果が得られます。
CloudFront-Forwarded-Protoヘッダをオリジンに転送した場合
ディストリビューションへの設定反映が完了したら実際にアクセスしてみて挙動を確認します。まずはCloudFront-Forwarded-Proto
ヘッダのみを転送するよう設定し、HTTPSでアクセスしたパターンです。CloudFront-Forwarded-Proto: httpsが確認できます。
$ curl -i https://www.example.com/ HTTP/2 200 content-type: text/html; charset=UTF-8 content-length: 394 date: Tue, 31 Jul 2018 12:56:22 GMT server: Apache/2.4.27 (Amazon) PHP/5.6.35 x-powered-by: PHP/5.6.35 x-cache: Miss from cloudfront via: 1.1 2c6248542582010bc022ce33969f1509.cloudfront.net (CloudFront) x-amz-cf-id: Zgz5u72QEN0IrHFsOoxr1NtvRUHDtdIHoiFU5SbIdeg9mH9wIvo2Ew== X-Forwarded-For: 1.XX.XX.XX, 13.113.203.179 X-Forwarded-Proto: https X-Forwarded-Port: 443 Host: elb.example.com X-Amzn-Trace-Id: Root=1-5b605c76-7a88d87a99d630f6476324ec X-Amz-Cf-Id: v8l_t1gTnymMOfqfKqeAzMWp1Du9Jhr6u9X1NWWbGexNdygzEQ783g== CloudFront-Forwarded-Proto: https User-Agent: Amazon CloudFront Via: 2.0 2c6248542582010bc022ce33969f1509.cloudfront.net (CloudFront)
続いて同じ条件でHTTPでアクセスしてみます。オリジンのEC2側でCloudFront-Forwarded-Proto: httpとなっていることが確認できますね。
$ curl -i http://www.example.com/ HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8 Content-Length: 393 Connection: keep-alive Date: Tue, 31 Jul 2018 12:56:29 GMT Server: Apache/2.4.27 (Amazon) PHP/5.6.35 X-Powered-By: PHP/5.6.35 X-Cache: Miss from cloudfront Via: 1.1 00e04c56f991d537f76f3de8fe1a96c6.cloudfront.net (CloudFront) X-Amz-Cf-Id: QngdQjOG5Nmm3i1Pksr-nueL8b8UPTOCX_KqM3B7nxpRXxKfzAjavQ== X-Forwarded-For: 1.XX.XX.XX, 13.113.203.141 X-Forwarded-Proto: https X-Forwarded-Port: 443 Host: elb.example.com X-Amzn-Trace-Id: Root=1-5b605c7d-4407ad64d4136a087e34b128 X-Amz-Cf-Id: d670MYBiRVc8RKGzRHxE1PV8TaPBfyx6viMpsZ7lD55zuoriD3GEYQ== CloudFront-Forwarded-Proto: http User-Agent: Amazon CloudFront Via: 1.1 00e04c56f991d537f76f3de8fe1a96c6.cloudfront.net (CloudFront)
ついでにすべてのヘッダを転送するよう設定した場合についても挙動を確認してみます。HTTPSの場合ですがCloudFront-Forwarded-Proto: httpsが含まれていることがわかります。
$ curl -i https://www.example.com/ HTTP/2 200 content-type: text/html; charset=UTF-8 content-length: 552 date: Tue, 31 Jul 2018 12:57:19 GMT server: Apache/2.4.27 (Amazon) PHP/5.6.35 x-powered-by: PHP/5.6.35 x-cache: Miss from cloudfront via: 1.1 4a0e5a774647d45df2730ca2d10b4249.cloudfront.net (CloudFront) x-amz-cf-id: _rMfKWZR4juuRtip9gmVYFa0vhOhb9lallBS7WCibj_Myzr13HvZeA== X-Forwarded-For: 1.XX.XX.XX, 54.239.196.100 X-Forwarded-Proto: https X-Forwarded-Port: 443 Host: www.example.com X-Amzn-Trace-Id: Root=1-5b605caf-94db6f50192f3d1d39aff06f X-Amz-Cf-Id: PhGdLwE-wEpvG_7YllwcdIFBFd5XE2-s8RB5Nd7KIL_NuvcKZ1iuTw== User-Agent: curl/7.54.0 Via: 2.0 4a0e5a774647d45df2730ca2d10b4249.cloudfront.net (CloudFront) CloudFront-Is-Mobile-Viewer: false CloudFront-Is-Tablet-Viewer: false CloudFront-Is-SmartTV-Viewer: false CloudFront-Is-Desktop-Viewer: true CloudFront-Viewer-Country: JP Accept: */* CloudFront-Forwarded-Proto: https
続いてすべてのヘッダヲ転送するようにした、HTTPの場合です。こちらも意図通りCloudFront-Forwarded-Proto: httpが確認できますね。
$ curl -i http://www.example.com/ HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8 Content-Length: 550 Connection: keep-alive Date: Tue, 31 Jul 2018 12:57:40 GMT Server: Apache/2.4.27 (Amazon) PHP/5.6.35 X-Powered-By: PHP/5.6.35 X-Cache: Miss from cloudfront Via: 1.1 7b63dc372a4330b28d4dd1f11ec139a7.cloudfront.net (CloudFront) X-Amz-Cf-Id: j1Qd_EperFor-kAN3jBbUrIj8DGuoB_f_d7VlJt_ie--_vGEU1CEHA== X-Forwarded-For: 1.XX.XX.XX, 54.239.196.74 X-Forwarded-Proto: https X-Forwarded-Port: 443 Host: www.example.com X-Amzn-Trace-Id: Root=1-5b605cc4-4be8ec14d6f1a1a5a6108649 X-Amz-Cf-Id: ZOSaW4-P_OTVu2zRD60bf1Bh3hRw7kpfVy6Vw45MG5b9Sq648yBnoQ== User-Agent: curl/7.54.0 Via: 1.1 7b63dc372a4330b28d4dd1f11ec139a7.cloudfront.net (CloudFront) CloudFront-Is-Mobile-Viewer: false CloudFront-Is-Tablet-Viewer: false CloudFront-Is-SmartTV-Viewer: false CloudFront-Is-Desktop-Viewer: true CloudFront-Viewer-Country: JP Accept: */* CloudFront-Forwarded-Proto: http
まとめ
CloudFrontでCloudFront-Forwarded-Proto
ヘッダをオリジンに転送するよう設定し、オリジン側でリクエストに含まれるヘッダ情報を確認することで、クライアントがCloudFrontにどの通信プロトコルで接続しているのか判別できることを確認しました。
今回この「CloudFrontにどの通信プロトコルで接続しているかオリジン側で確認する」ことに至ったきっかけは、別エントリにまとめていますApacheのディレクトリ名補完時のリダイレクト処理に関する問題だったりします。このCloudFront-Forwarded-Proto
ヘッダをオリジン側で確認することで、クライアントからの通信がHTTPSかHTTPかを判断し、HTTPSならApache側で別のLocationにリダイレクトする際にもHTTPSにする、など設定できればディレクト名補完時のリダイレクトなどの処理もHTTPSで完結するようになるかと思います。